#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include "cmd_cp.h"
#include "cmd_handle.h"
#define DEBUG

// 将cp命令参数信息存储到文件信息结构体
int cmd_cp_parse_path(cmd_t *pcmd, cp_file_info_t *pfileinfo)
{
	if (pcmd == NULL || pfileinfo == NULL)
	{
		return -1;
	}

	strcpy(pfileinfo->src_path, pcmd->cmd_arg_list[0]);
	strcpy(pfileinfo->dest_path, pcmd->cmd_arg_list[1]);

#ifdef DEBUG
	printf("src path: < %s >\n", pfileinfo->src_path);
	printf("dest path: < %s >\n", pfileinfo->dest_path);
#endif
}

// 获取文件类型
enum file_type get_file_type(const char *path)
{
	int ret;
	struct stat stat_info;

	if (path == NULL)
	{
		return FT_ERROR;
	}

	ret = stat(path, &stat_info);
	if (ret == -1)
	{
		perror("stat():");
		return FT_ERROR;
	}

	if (S_ISDIR(stat_info.st_mode))
	{
		return FT_DIR;
	}
	else if (S_ISREG(stat_info.st_mode))
	{
		return FT_FILE;
	}
}

// 将文件类型存储在文件信息结构�?
int cmd_cp_parse_type(cp_file_info_t *pfileinfo)
{
	enum file_type ftype;

	if (pfileinfo == NULL)
	{
		return -1;
	}
	ftype = get_file_type(pfileinfo->src_path);
	if (ftype == FT_ERROR || ftype == FT_UNKNOWN)
	{
		return -1;
	}
	else
	{
		pfileinfo->src_ftype = ftype;
	}

#ifdef DEBUG
	if (pfileinfo->src_ftype == FT_FILE)
	{
		printf("this is file\n");
	}
	else if (pfileinfo->src_ftype == FT_DIR)
	{
		printf("this is dir\n");
	}
#endif

	return 0;
}

// 分发函数 根据源文件是普通文件还是目录来调用不同函数
int cmd_cp_dispatch(cp_file_info_t *pfileinfo)
{
	if (pfileinfo->src_ftype == FT_FILE)
	{
		return cmd_cp_file(pfileinfo->src_path, pfileinfo->dest_path);
	}
	else if (pfileinfo->src_ftype == FT_DIR)
	{

		return cmd_cp_directory(pfileinfo->src_path, pfileinfo->dest_path);
	}
}

// 文件复制
int cmd_cp_file(const char *src, const char *dest)
{
	FILE *fp_src = NULL, *fp_dest = NULL;
	size_t rbytes = 0, wbytes = 0;
	char buffer[SZ_BUFFER] = {0};

	if (src == NULL || dest == NULL)
	{
		return -1;
	}

	fp_src = fopen(src, "r");
	fp_dest = fopen(dest, "w+");
	if (fp_src == NULL || fp_dest == NULL)
	{
		return -1;
	}

	for (;;)
	{
		// 读源文件到缓存区
		rbytes = fread(buffer, sizeof(char), SZ_BUFFER, fp_src);
		if (rbytes != 0)
		{
			// 从缓存中向目标文件写
			wbytes = fwrite(buffer, sizeof(char), rbytes, fp_dest);
			if (rbytes != wbytes)
			{
				perror("[ERROR] fwrite():");
				return -1;
			}
		}
		else
		{
			break;
		}
	}

	fclose(fp_src);
	fclose(fp_dest);

	return 0;
}

// 目录复制
int cmd_cp_directory(char *src, char *dest)
{
	enum file_type ftype;
	int ret;
	DIR *pdir = NULL;
	struct dirent *pdirent = NULL;
	cp_file_info_t info;

	// 获取文件类型
	ftype = get_file_type(src);
	if (ftype == FT_ERROR || ftype == FT_UNKNOWN)
	{
		return -1;
	}

	// 检测到为目录文件
	if (ftype == FT_DIR)
	{
		ret = mkdir(dest, 0777);
		if (ret == -1)
		{
			perror("mkdir():");
			return -1;
		}

		pdir = opendir(src);
		if (pdir == NULL)
		{
			perror("opendir():");
			return -1;
		}

		while (1)
		{
			// 读取目录
			pdirent = readdir(pdir);
			if (pdirent == NULL)
			{
				break;
			}

			//  ls -a 显示当前所有文件
			if (strcmp(pdirent->d_name, ".") == 0 || strcmp(pdirent->d_name, "..") == 0)
			{
				continue;
			}
#ifdef DEBUG
			printf("[DBUG]: filename: %s\n", pdirent->d_name);
#endif
			make_path(&info, src, dest, pdirent->d_name);
#ifdef DEBUG
			printf("[DBUG]: src path: %s\n", info.src_path);
			printf("[DBUG]: dest path: %s\n", info.dest_path);
#endif
			// 对下一级目录进行判断处理
			info.src_ftype = get_file_type(info.src_path);

			if (info.src_ftype == FT_DIR)
			{
				cmd_cp_directory(info.src_path, info.dest_path);
			}
			else if (info.src_ftype == FT_FILE)
			{
				cmd_cp_file(info.src_path, info.dest_path);
			}
		}
		closedir(pdir);
	}
	else if (ftype == FT_FILE)
	{
		cmd_cp_file(src, dest);
	}
	else
	{
		return 0;
	}
}

// 对路径的处理 拼接成下一级目录
void make_path(cp_file_info_t *pinfo, const char *spath, const char *dpath, const char *filename)
{
	memset(pinfo->src_path, 0, sizeof(pinfo->src_path));
	memset(pinfo->dest_path, 0, sizeof(pinfo->dest_path));

	strcpy(pinfo->src_path, spath);
	strcat(pinfo->src_path, "/");
	strcat(pinfo->src_path, filename);

	strcpy(pinfo->dest_path, dpath);
	strcat(pinfo->dest_path, "/");
	strcat(pinfo->dest_path, filename);
}

int cmd_cp_execute(cmd_t *pcmd)
{
	struct cp_file_info fileinfo;
	int ret;

	if (pcmd->cmd_arg_count != 2)
	{
		return -1;
	}

	// 解析cp命令参数 将参数存储到文件信息结构�?
	ret = cmd_cp_parse_path(pcmd, &fileinfo);
	if (ret == -1)
	{
		return -1;
	}

	// 将文件类型写到文件信息结构体
	ret = cmd_cp_parse_type(&fileinfo);
	if (ret == -1)
	{
		return -1;
	}

	// 分发 普通文件还是目录文件
	ret = cmd_cp_dispatch(&fileinfo);
	if (ret == -1)
	{
		return -1;
	}

	return 0;
}
